"""
数据分析插件单元测试
"""
import unittest
import numpy as np
import sys
import os
from unittest.mock import Mock, patch

# 添加src目录到Python路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))

from plugins.data_analysis.data_analysis import PearsonCorrelation, SpearmanCorrelation, DataMerger
from plugins.base_plugin import BasePlugin


class TestDataAnalysisPlugins(unittest.TestCase):
    """测试数据分析插件"""

    def setUp(self):
        """设置测试环境"""
        # 模拟插件配置
        self.mock_config = {
            "config": {
                "column_names": ["value1", "value2", "value3"]
            }
        }

    def test_pearson_correlation_normal_data(self):
        """测试Pearson相关性分析 - 正常数据"""
        # 创建插件实例
        plugin = PearsonCorrelation("test_correlation", self.mock_config)
        
        # 准备测试数据 - 具有明确相关性的数据
        # value1和value2是完全正相关的
        # value1和value3是完全负相关的
        test_data = [
            {"value1": 1.0, "value2": 2.0, "value3": -1.0},
            {"value1": 2.0, "value2": 4.0, "value3": -2.0},
            {"value1": 3.0, "value2": 6.0, "value3": -3.0},
            {"value1": 4.0, "value2": 8.0, "value3": -4.0},
            {"value1": 5.0, "value2": 10.0, "value3": -5.0}
        ]
        
        # 执行测试
        result = plugin.execute(test_data)
        
        # 验证结果
        self.assertIsNotNone(result["correlation_matrix"])
        self.assertEqual(result["pearson_correlation_by"], "test_correlation")
        self.assertEqual(result["columns_analyzed"], ["value1", "value2", "value3"])
        
        # 验证相关性结果
        correlation_matrix = result["correlation_matrix"]
        # 完全正相关应为1.0
        self.assertAlmostEqual(correlation_matrix["value1"]["value2"], 1.0, places=6)
        # 完全负相关应为-1.0
        self.assertAlmostEqual(correlation_matrix["value1"]["value3"], -1.0, places=6)
        # 自身相关应为1.0
        self.assertAlmostEqual(correlation_matrix["value1"]["value1"], 1.0, places=6)

    def test_pearson_correlation_partial_correlation(self):
        """测试Pearson相关性分析 - 部分相关的数据"""
        plugin = PearsonCorrelation("test_correlation", self.mock_config)
        
        # 准备测试数据 - 具有部分相关性的数据
        test_data = [
            {"value1": 1.0, "value2": 1.1, "value3": 3.0},
            {"value1": 2.0, "value2": 2.2, "value3": 2.8},
            {"value1": 3.0, "value2": 3.1, "value3": 2.7},
            {"value1": 4.0, "value2": 4.3, "value3": 2.5},
            {"value1": 5.0, "value2": 5.2, "value3": 2.3}
        ]
        
        result = plugin.execute(test_data)
        
        # 验证结果
        self.assertIsNotNone(result["correlation_matrix"])
        
        # value1和value2应该有高正相关
        self.assertTrue(result["correlation_matrix"]["value1"]["value2"] > 0.9)
        # value1和value3应该有负相关
        self.assertTrue(result["correlation_matrix"]["value1"]["value3"] < 0)

    def test_pearson_correlation_with_missing_values(self):
        """测试Pearson相关性分析 - 包含缺失值的数据"""
        plugin = PearsonCorrelation("test_correlation", self.mock_config)
        
        # 准备测试数据 - 包含缺失值
        test_data = [
            {"value1": 1.0, "value2": 2.0, "value3": -1.0},
            {"value1": 2.0, "value2": None, "value3": -2.0},  # value2缺失
            {"value1": 3.0, "value2": 6.0, "value3": -3.0},
            {"value1": None, "value2": 8.0, "value3": -4.0},  # value1缺失
            {"value1": 5.0, "value2": 10.0, "value3": -5.0}
        ]
        
        result = plugin.execute(test_data)
        
        # 验证结果 - 插件应该能处理缺失值
        self.assertIsNotNone(result["correlation_matrix"])

    def test_pearson_correlation_auto_detect_columns(self):
        """测试Pearson相关性分析 - 自动检测数值列"""
        # 使用空配置，让插件自动检测数值列
        plugin = PearsonCorrelation("test_correlation", {"config": {}})
        
        # 准备测试数据 - 包含数值列和非数值列
        test_data = [
            {"value1": 1.0, "value2": 2.0, "category": "A", "timestamp": 1000},
            {"value1": 2.0, "value2": 4.0, "category": "B", "timestamp": 1060},
            {"value1": 3.0, "value2": 6.0, "category": "A", "timestamp": 1120}
        ]
        
        result = plugin.execute(test_data)
        
        # 验证结果 - 应该只分析数值列
        self.assertIsNotNone(result["correlation_matrix"])
        # 检查是否只分析了数值列
        analyzed_columns = result["columns_analyzed"]
        self.assertTrue(all(col in ["value1", "value2", "timestamp"] for col in analyzed_columns))

    def test_pearson_correlation_insufficient_data(self):
        """测试Pearson相关性分析 - 数据不足的情况"""
        plugin = PearsonCorrelation("test_correlation", self.mock_config)
        
        # 准备测试数据 - 只有一行数据
        test_data = [
            {"value1": 1.0, "value2": 2.0, "value3": -1.0}
        ]
        
        result = plugin.execute(test_data)
        
        # 验证结果 - 应该返回错误信息
        self.assertIsNone(result["correlation_matrix"])
        self.assertEqual(result["error"], "Insufficient data points for correlation analysis")

    def test_pearson_correlation_invalid_input(self):
        """测试Pearson相关性分析 - 无效输入"""
        plugin = PearsonCorrelation("test_correlation", self.mock_config)
        
        # 测试空数据
        empty_result = plugin.execute([])
        self.assertIsNone(empty_result["correlation_matrix"])
        
        # 测试非列表输入
        invalid_result = plugin.execute({"not_a_list": True})
        self.assertIsNone(invalid_result["correlation_matrix"])

    def test_pearson_correlation_config_validation(self):
        """测试Pearson相关性分析 - 配置验证"""
        # 测试有效配置
        valid_plugin = PearsonCorrelation("test_correlation", self.mock_config)
        self.assertTrue(valid_plugin.validate_config())
        
        # 测试无效配置 - column_names不是列表
        invalid_config = {"config": {"column_names": "not_a_list"}}
        invalid_plugin = PearsonCorrelation("test_correlation", invalid_config)
        self.assertFalse(invalid_plugin.validate_config())

    def test_spearman_correlation_normal_data(self):
        """测试Spearman相关性分析 - 正常数据"""
        # 创建插件实例
        plugin = SpearmanCorrelation("test_spearman", self.mock_config)
        
        # 准备测试数据 - 具有明确相关性的数据
        test_data = [
            {"value1": 1.0, "value2": 2.0, "value3": -1.0},
            {"value1": 2.0, "value2": 4.0, "value3": -2.0},
            {"value1": 3.0, "value2": 6.0, "value3": -3.0},
            {"value1": 4.0, "value2": 8.0, "value3": -4.0},
            {"value1": 5.0, "value2": 10.0, "value3": -5.0}
        ]
        
        # 执行测试
        result = plugin.execute(test_data)
        
        # 验证结果
        self.assertIsNotNone(result["correlation_matrix"])
        self.assertEqual(result["spearman_correlation_by"], "test_spearman")
        
        # 验证相关性结果
        correlation_matrix = result["correlation_matrix"]
        # 完全正相关应为1.0
        self.assertAlmostEqual(correlation_matrix["value1"]["value2"], 1.0, places=6)
        # 完全负相关应为-1.0
        self.assertAlmostEqual(correlation_matrix["value1"]["value3"], -1.0, places=6)

    def test_spearman_correlation_monotonic_relationship(self):
        """测试Spearman相关性分析 - 单调但非线性关系"""
        plugin = SpearmanCorrelation("test_spearman", self.mock_config)
        
        # 准备测试数据 - 具有单调但非线性关系
        # value1和value2是单调关系但非线性
        test_data = [
            {"value1": 1.0, "value2": 1.0, "value3": 10.0},
            {"value1": 2.0, "value2": 2.0, "value3": 9.0},
            {"value1": 3.0, "value2": 4.0, "value3": 8.0},
            {"value1": 4.0, "value2": 8.0, "value3": 7.0},
            {"value1": 5.0, "value2": 16.0, "value3": 6.0}
        ]
        
        result = plugin.execute(test_data)
        
        # 验证结果
        self.assertIsNotNone(result["correlation_matrix"])
        
        # value1和value2应该有高正相关（Spearman）
        self.assertAlmostEqual(result["correlation_matrix"]["value1"]["value2"], 1.0, places=6)
        # value1和value3应该有高负相关（Spearman）
        self.assertAlmostEqual(result["correlation_matrix"]["value1"]["value3"], -1.0, places=6)

    def test_spearman_correlation_with_outliers(self):
        """测试Spearman相关性分析 - 包含异常值的数据"""
        plugin = SpearmanCorrelation("test_spearman", self.mock_config)
        
        # 准备测试数据 - 包含异常值
        test_data = [
            {"value1": 1.0, "value2": 1.0, "value3": 10.0},
            {"value1": 2.0, "value2": 2.0, "value3": 20.0},
            {"value1": 3.0, "value2": 3.0, "value3": 30.0},
            {"value1": 4.0, "value2": 4.0, "value3": 40.0},
            {"value1": 5.0, "value2": 100.0, "value3": 50.0}  # 异常值
        ]
        
        result = plugin.execute(test_data)
        
        # 验证结果 - Spearman应该对异常值不太敏感
        self.assertIsNotNone(result["correlation_matrix"])
        # value1和value2在Spearman相关中仍然应该有较高的相关性
        self.assertTrue(result["correlation_matrix"]["value1"]["value2"] > 0.8)

    def test_spearman_correlation_config_validation(self):
        """测试Spearman相关性分析 - 配置验证"""
        # 测试有效配置
        valid_plugin = SpearmanCorrelation("test_spearman", self.mock_config)
        self.assertTrue(valid_plugin.validate_config())
        
        # 测试无效配置 - column_names不是列表
        invalid_config = {"config": {"column_names": "not_a_list"}}
        invalid_plugin = SpearmanCorrelation("test_spearman", invalid_config)
        self.assertFalse(invalid_plugin.validate_config())


    def test_data_merger_inner_join(self):
        """测试数据合并插件 - 内连接"""
        # 创建插件实例 - 使用内连接
        config = {"config": {"primary_key": "id", "merge_type": "inner"}}
        plugin = DataMerger("test_merger", config)
        
        # 准备测试数据
        data1 = [
            {"id": 1, "name": "Alice", "age": 30},
            {"id": 2, "name": "Bob", "age": 25},
            {"id": 3, "name": "Charlie", "age": 35}
        ]
        
        data2 = [
            {"id": 1, "department": "HR", "salary": 5000},
            {"id": 2, "department": "IT", "salary": 6000},
            {"id": 4, "department": "Finance", "salary": 5500}
        ]
        
        # 执行测试
        result = plugin.execute({"data1": data1, "data2": data2})
        
        # 验证结果
        self.assertIsNotNone(result["merged_data"])
        self.assertEqual(result["merged_by"], "test_merger")
        self.assertEqual(result["primary_key"], "id")
        self.assertEqual(result["merge_type"], "inner")
        
        # 内连接应该只包含id=1和id=2的记录
        self.assertEqual(len(result["merged_data"]), 2)
        # 检查合并的数据
        merged_ids = [record["id"] for record in result["merged_data"]]
        self.assertIn(1, merged_ids)
        self.assertIn(2, merged_ids)
        self.assertNotIn(3, merged_ids)
        self.assertNotIn(4, merged_ids)
        
        # 检查合并后的数据字段是否正确
        for record in result["merged_data"]:
            if record["id"] == 1:
                self.assertEqual(record["name"], "Alice")
                self.assertEqual(record["department"], "HR")
            elif record["id"] == 2:
                self.assertEqual(record["name"], "Bob")
                self.assertEqual(record["department"], "IT")
    
    def test_data_merger_left_join(self):
        """测试数据合并插件 - 左连接"""
        # 创建插件实例 - 使用左连接
        config = {"config": {"primary_key": "id", "merge_type": "left"}}
        plugin = DataMerger("test_merger", config)
        
        # 准备测试数据
        data1 = [
            {"id": 1, "name": "Alice", "age": 30},
            {"id": 2, "name": "Bob", "age": 25},
            {"id": 3, "name": "Charlie", "age": 35}
        ]
        
        data2 = [
            {"id": 1, "department": "HR", "salary": 5000},
            {"id": 2, "department": "IT", "salary": 6000},
            {"id": 4, "department": "Finance", "salary": 5500}
        ]
        
        # 执行测试
        result = plugin.execute({"data1": data1, "data2": data2})
        
        # 验证结果
        self.assertIsNotNone(result["merged_data"])
        # 左连接应该包含data1的所有记录（3条）
        self.assertEqual(len(result["merged_data"]), 3)
        
        # 检查合并的数据
        merged_ids = [record["id"] for record in result["merged_data"]]
        self.assertIn(1, merged_ids)
        self.assertIn(2, merged_ids)
        self.assertIn(3, merged_ids)
        self.assertNotIn(4, merged_ids)
    
    def test_data_merger_right_join(self):
        """测试数据合并插件 - 右连接"""
        # 创建插件实例 - 使用右连接
        config = {"config": {"primary_key": "id", "merge_type": "right"}}
        plugin = DataMerger("test_merger", config)
        
        # 准备测试数据
        data1 = [
            {"id": 1, "name": "Alice", "age": 30},
            {"id": 2, "name": "Bob", "age": 25},
            {"id": 3, "name": "Charlie", "age": 35}
        ]
        
        data2 = [
            {"id": 1, "department": "HR", "salary": 5000},
            {"id": 2, "department": "IT", "salary": 6000},
            {"id": 4, "department": "Finance", "salary": 5500}
        ]
        
        # 执行测试
        result = plugin.execute({"data1": data1, "data2": data2})
        
        # 验证结果
        self.assertIsNotNone(result["merged_data"])
        # 右连接应该包含data2的所有记录（3条）
        self.assertEqual(len(result["merged_data"]), 3)
        
        # 检查合并的数据
        merged_ids = [record["id"] for record in result["merged_data"]]
        self.assertIn(1, merged_ids)
        self.assertIn(2, merged_ids)
        self.assertNotIn(3, merged_ids)
        self.assertIn(4, merged_ids)
    
    def test_data_merger_outer_join(self):
        """测试数据合并插件 - 外连接"""
        # 创建插件实例 - 使用外连接
        config = {"config": {"primary_key": "id", "merge_type": "outer"}}
        plugin = DataMerger("test_merger", config)
        
        # 准备测试数据
        data1 = [
            {"id": 1, "name": "Alice", "age": 30},
            {"id": 2, "name": "Bob", "age": 25},
            {"id": 3, "name": "Charlie", "age": 35}
        ]
        
        data2 = [
            {"id": 1, "department": "HR", "salary": 5000},
            {"id": 2, "department": "IT", "salary": 6000},
            {"id": 4, "department": "Finance", "salary": 5500}
        ]
        
        # 执行测试
        result = plugin.execute({"data1": data1, "data2": data2})
        
        # 验证结果
        self.assertIsNotNone(result["merged_data"])
        # 外连接应该包含所有记录（4条）
        self.assertEqual(len(result["merged_data"]), 4)
        
        # 检查合并的数据
        merged_ids = [record["id"] for record in result["merged_data"]]
        self.assertIn(1, merged_ids)
        self.assertIn(2, merged_ids)
        self.assertIn(3, merged_ids)
        self.assertIn(4, merged_ids)
    
    def test_data_merger_invalid_input(self):
        """测试数据合并插件 - 无效输入"""
        # 创建插件实例
        config = {"config": {"primary_key": "id", "merge_type": "inner"}}
        plugin = DataMerger("test_merger", config)
        
        # 测试缺少data1和data2的输入
        invalid_input1 = {"not_data": "invalid"}
        result1 = plugin.execute(invalid_input1)
        self.assertIsNone(result1["merged_data"])
        self.assertEqual(result1["error"], "Insufficient data sources")
        
        # 测试非列表数据源
        invalid_input2 = {"data1": "not_a_list", "data2": ["valid"]}
        result2 = plugin.execute(invalid_input2)
        self.assertIsNone(result2["merged_data"])
        self.assertEqual(result2["error"], "Data sources must be lists")
    
    def test_data_merger_missing_primary_key(self):
        """测试数据合并插件 - 缺少主键配置"""
        # 创建插件实例 - 不提供primary_key
        config = {"config": {"merge_type": "inner"}}
        plugin = DataMerger("test_merger", config)
        
        # 准备测试数据
        data1 = [{"id": 1, "name": "Alice"}]
        data2 = [{"id": 1, "department": "HR"}]
        
        # 执行测试
        result = plugin.execute({"data1": data1, "data2": data2})
        
        # 验证结果
        self.assertIsNone(result["merged_data"])
        self.assertEqual(result["error"], "Primary key not specified")
    
    def test_data_merger_config_validation(self):
        """测试数据合并插件 - 配置验证"""
        # 测试有效配置
        valid_config = {"config": {"primary_key": "id", "merge_type": "inner"}}
        valid_plugin = DataMerger("test_merger", valid_config)
        self.assertTrue(valid_plugin.validate_config())
        
        # 测试无效配置 - 缺少primary_key
        invalid_config1 = {"config": {"merge_type": "inner"}}
        invalid_plugin1 = DataMerger("test_merger", invalid_config1)
        self.assertFalse(invalid_plugin1.validate_config())
        
        # 测试无效配置 - primary_key不是字符串
        invalid_config2 = {"config": {"primary_key": 123, "merge_type": "inner"}}
        invalid_plugin2 = DataMerger("test_merger", invalid_config2)
        self.assertFalse(invalid_plugin2.validate_config())
        
        # 测试无效配置 - 无效的merge_type
        invalid_config3 = {"config": {"primary_key": "id", "merge_type": "invalid_type"}}
        invalid_plugin3 = DataMerger("test_merger", invalid_config3)
        self.assertFalse(invalid_plugin3.validate_config())
        
    def test_data_merger_multiple_primary_keys(self):
        """测试数据合并插件 - 多个主键"""
        # 创建插件实例 - 使用多个主键
        config = {"config": {"primary_key": ["component_id", "timestamp"], "merge_type": "inner"}}
        plugin = DataMerger("test_merger", config)
        
        # 准备测试数据
        data1 = [
            {"component_id": "A", "timestamp": 1, "name": "Component A", "temp": 70},
            {"component_id": "A", "timestamp": 2, "name": "Component A", "temp": 72},
            {"component_id": "B", "timestamp": 1, "name": "Component B", "temp": 65}
        ]
        
        data2 = [
            {"component_id": "A", "timestamp": 1, "status": "OK", "pressure": 100},
            {"component_id": "A", "timestamp": 2, "status": "WARNING", "pressure": 105},
            {"component_id": "B", "timestamp": 1, "status": "OK", "pressure": 95}
        ]
        
        # 执行测试
        result = plugin.execute({"data1": data1, "data2": data2})
        
        # 验证结果
        self.assertIsNotNone(result["merged_data"])
        self.assertEqual(result["merged_by"], "test_merger")
        self.assertEqual(result["primary_key"], ["component_id", "timestamp"])
        self.assertEqual(result["merge_type"], "inner")
        
        # 内连接应该包含所有记录（3条）
        self.assertEqual(len(result["merged_data"]), 3)
        
        # 检查合并后的数据字段是否正确
        for record in result["merged_data"]:
            if record["component_id"] == "A" and record["timestamp"] == 1:
                self.assertEqual(record["name"], "Component A")
                self.assertEqual(record["temp"], 70)
                self.assertEqual(record["status"], "OK")
                self.assertEqual(record["pressure"], 100)
            elif record["component_id"] == "A" and record["timestamp"] == 2:
                self.assertEqual(record["name"], "Component A")
                self.assertEqual(record["temp"], 72)
                self.assertEqual(record["status"], "WARNING")
                self.assertEqual(record["pressure"], 105)
            elif record["component_id"] == "B" and record["timestamp"] == 1:
                self.assertEqual(record["name"], "Component B")
                self.assertEqual(record["temp"], 65)
                self.assertEqual(record["status"], "OK")
                self.assertEqual(record["pressure"], 95)
    
    def test_data_merger_multiple_primary_keys_config_validation(self):
        """测试数据合并插件 - 多个主键配置验证"""
        # 测试有效配置 - 多个主键
        valid_config = {"config": {"primary_key": ["id", "type"], "merge_type": "inner"}}
        valid_plugin = DataMerger("test_merger", valid_config)
        self.assertTrue(valid_plugin.validate_config())
        
        # 测试无效配置 - 空主键列表
        invalid_config1 = {"config": {"primary_key": [], "merge_type": "inner"}}
        invalid_plugin1 = DataMerger("test_merger", invalid_config1)
        self.assertFalse(invalid_plugin1.validate_config())
        
        # 测试无效配置 - 主键列表包含非字符串元素
        invalid_config2 = {"config": {"primary_key": ["id", 123], "merge_type": "inner"}}
        invalid_plugin2 = DataMerger("test_merger", invalid_config2)
        self.assertFalse(invalid_plugin2.validate_config())
        
        # 测试无效配置 - 空字符串主键
        invalid_config3 = {"config": {"primary_key": "", "merge_type": "inner"}}
        invalid_plugin3 = DataMerger("test_merger", invalid_config3)
        self.assertFalse(invalid_plugin3.validate_config())


if __name__ == "__main__":
    unittest.main()